package conference.view;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

import org.gstreamer.Element;
import org.gstreamer.swing.VideoComponent;

import conference.model.ConferenceState.NOTIFICATION_TYPE;
import conference.controller.ButtonHandling;
import conference.model.Client;
import conference.model.ClientList;
import conference.model.ConferenceStateClientSide;
import conference.model.ConferenceStateClientSide.CALL_STATE;

/**
*
* a view class to handle the GUI of the client program
* 
*/
public class ConferenceGUI implements Observer {

	private final ConferenceStateClientSide conference;
	private ButtonHandling buttonHandling;
	
	private JFrame mainFrame = new JFrame("JConference");
	
	private VideoComponent vcPrivateVideo = new VideoComponent();
    private VideoComponent vcMyVideo = new VideoComponent();
    private VideoComponent vcBroadcastVideo = new VideoComponent();
    
    private JPanel listPanel = new JPanel();
    private JPanel buttonsPanel = new JPanel();
    
    public JButton btStop = new JButton("Stop");
    public final JButton btMute = new JButton("Mute (off)");
    public final JTextField tfIpServer = new JTextField("notSet");//server ip 
    public JButton btConnect = new JButton("Connect");
    public final JTextField tfNickname = new JTextField("nickname");
    public JButton btBroadcast = new JButton("Broadcast");
    
    // Creating the "callFrame"
    public JFrame callFrame = new JFrame("Incomming Call !");
    public final JTextField tfCall = new JTextField("Incomming Call !");
    public JButton btAcceptCall = new JButton("Accept");
    public JButton btRejectCall = new JButton("Reject");
    // ------------------------
    
    public boolean isMute = false;
    
    public SenderStreamUDP senderStream;
    public RecieverStreamUDP broadcastRecieverStream;
    public RecieverStreamUDP privateChatRecieverStream;
    public Ring ring;
    
	public ConferenceGUI(final ConferenceStateClientSide conference, ButtonHandling buttonHandling){
		this.conference=conference;
		this.buttonHandling=buttonHandling;
		this.conference.addObserver(this);
		
		this.mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
		// I - We create and fill the Users List Panel and the Buttons Panel
		
		// 1) The Users List Panel (on the left) :
		listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS));
        tfIpServer.setText(conference.getServerIp());
        tfIpServer.setToolTipText("Specify the IP adress to the server");
        JLabel listLabel = new JLabel("List of Users");
        this.listPanel.add(listLabel);
        
        // 2) The Buttons Panel (at the bottom)
        GridLayout glButtons = new GridLayout(1,10);
        glButtons.setHgap(5);
        glButtons.setVgap(5);
        buttonsPanel.setLayout(glButtons);
        // We add ActionListeners to the buttons:
        btStop.addActionListener(buttonHandling);
        btMute.addActionListener(buttonHandling);
        btBroadcast.addActionListener(buttonHandling);
        btConnect.addActionListener(buttonHandling);
        // We add the buttons to the Buttons Panel:
        buttonsPanel.add(btStop);
        buttonsPanel.add(btMute);
        buttonsPanel.add(btBroadcast);
        buttonsPanel.add(tfIpServer);
        buttonsPanel.add(tfNickname);
        buttonsPanel.add(btConnect);

        // II - We place all the components (previously created panels and video components) in the frame
        
        JPanel panTopMiddle = new JPanel();
        panTopMiddle.setLayout(new BoxLayout(panTopMiddle, BoxLayout.Y_AXIS));
 		changeBoth(vcPrivateVideo);
 		changeBoth(vcMyVideo);
 		vcPrivateVideo.setMinimumSize(new Dimension(160,120));
 		vcPrivateVideo.setPreferredSize(new Dimension(160,120));
 		vcMyVideo.setMinimumSize(new Dimension(160,120));
 		vcMyVideo.setPreferredSize(new Dimension(160,120));
        panTopMiddle.add(vcPrivateVideo);
        panTopMiddle.add(vcMyVideo);
        
        JPanel panTop = new JPanel();
        panTop.setLayout(new BoxLayout(panTop, BoxLayout.X_AXIS));
        
 		changeHeight(listPanel);
        changeBoth(vcBroadcastVideo);
        vcBroadcastVideo.setMinimumSize(new Dimension(320,240));
        vcBroadcastVideo.setPreferredSize(new Dimension(320,240));
        panTop.add(listPanel);
        panTop.add(panTopMiddle);
        panTop.add(vcBroadcastVideo);
        
        Container contentPane = mainFrame.getContentPane();
        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
        changeWidth(buttonsPanel);
        contentPane.add(panTop);
        contentPane.add(buttonsPanel);
        
        mainFrame.pack();
        mainFrame.setVisible(true);
		
        // We set the video and audio streams
		this.senderStream = new SenderStreamUDP(vcMyVideo.getElement(), conference);
		this.broadcastRecieverStream = new RecieverStreamUDP(vcBroadcastVideo, conference.getReceivingPortBroadcastVideo(), conference.getReceivingPortBroadcastAudio());
		this.privateChatRecieverStream = new RecieverStreamUDP(vcPrivateVideo, conference.getReceivingPortPrivateAcceptorVideo(), conference.getReceivingPortPrivateAcceptorAudio());
		
		
		// We set the "incoming call" window
		btAcceptCall.addActionListener(buttonHandling);
		btRejectCall.addActionListener(buttonHandling);
		callFrame.getContentPane().add(tfCall);
		callFrame.getContentPane().add(btAcceptCall);
		callFrame.getContentPane().add(btRejectCall);
		callFrame.setVisible(false);
	}
	
	private static void changeWidth(JComponent comp) {
	    comp.setAlignmentX(Component.CENTER_ALIGNMENT);
	    comp.setAlignmentY(Component.CENTER_ALIGNMENT);
	    Dimension dim = comp.getPreferredSize();
	    dim.width = Integer.MAX_VALUE;
	    comp.setMaximumSize(dim);
	}
	
	private static void changeHeight(JComponent comp) {
	    comp.setAlignmentX(Component.CENTER_ALIGNMENT);
	    comp.setAlignmentY(Component.CENTER_ALIGNMENT);
	    Dimension dim = comp.getPreferredSize();
	    dim.height = Integer.MAX_VALUE;
	    comp.setMaximumSize(dim);
	}
	
	private static void changeBoth(JComponent comp) {
	    comp.setAlignmentX(Component.CENTER_ALIGNMENT);
	    comp.setAlignmentY(Component.CENTER_ALIGNMENT);
	    Dimension dim = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
	    comp.setMaximumSize(dim);
	}
	
	/**
	*
	* updates the display of the user list
	* 
	* @param cl the new client list that must be displayed
	* 
	*/
	public void updateUsersList(ClientList cl){
		int size = cl.size();
		if(size>0){
			listPanel.removeAll();
			JLabel listLabel = new JLabel(" List of Users");
	        listPanel.add(listLabel);
			for(int i=0; i<size; i++){
				JButton bt = new JButton(cl.get(i).getNick());
				bt.setName(cl.get(i).getipAddr());
				bt.addActionListener(buttonHandling.list);
				listPanel.setVisible(false);
				listPanel.add(bt);
				listPanel.setVisible(true);
			}
		}
		else{
			System.out.println("no clients in user list!");
		}
		if(conference.userIsCurrentBroadcaster()){
			senderStream.setBroadcastAndPrivateDestinationHosts(cl,conference.getPrivateTalkClient());
		}
	}
	
	/**
	*
	* returns the "video of myself" GStreamer Element
	* 
	* @return the Element of vcMyVideo
	* 
	*/
	public Element getVideoElement(){
		return vcMyVideo.getElement();
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		
		if(arg1==NOTIFICATION_TYPE.CLIENT_LIST_UPDATE){
			updateUsersList(conference.getList());
			//if client is broadcaster..
			if(conference.getCurrentBroadCaster()!=null&&conference.getCurrentBroadCaster().equals(conference.getThisClient())){
				//reload sending stream
				senderStream.setBroadcastAndPrivateDestinationHosts(conference.getList(),conference.getPrivateTalkClient());
			}
			
		}
		if(arg1==NOTIFICATION_TYPE.CONFERENCE_PARTICIPATION_UPDATE){
			switchConnectDisconnect(conference.isInConference());
			if(conference.isInConference()){
				senderStream.startPipelines();
				broadcastRecieverStream.startPipelines();
				privateChatRecieverStream.startPipelines();
			}else{
				senderStream.stopPipelines();
				broadcastRecieverStream.stopPipelines();
				privateChatRecieverStream.stopPipelines();
			}
		}
		if(arg1==NOTIFICATION_TYPE.PRIVATE_TALK_UPDATE){
			//TODO: check if initiator or initiator
			System.out.println("privateTalkUpdate: "+conference.getPrivateTalkClient());//TODO: see if this happens
			if(conference.callState==CALL_STATE.IN_CALL){
				//start streaming private video and audio
				System.out.println("privateTalkUpdate : in_call");
				senderStream.setBroadcastAndPrivateDestinationHosts(conference.getList(), conference.getPrivateTalkClient());
			}
			else if(conference.callState==CALL_STATE.NOT_IN_CALL){
				System.out.println("privateTalkUpdate : not_in_call");
				if(conference.clientIsInitiator==false){
					privateChatRecieverStream.setInputPorts(conference.getReceivingPortPrivateInitiatorVideo(), conference.getReceivingPortPrivateInitiatorAudio());
					conference.clientIsInitiator=true;
				}
				senderStream.setBroadcastAndPrivateDestinationHosts(conference.getList(), new Client("192.0.0.10","fake"));
			}
		}
		if(arg1==NOTIFICATION_TYPE.BROADCASTER_UPDATE){
			//for broadcaster: if client is new broadcaster:
			if(conference.getCurrentBroadCaster().equals(conference.getThisClient())){
				//let sending stream start to send to the entire list of participants
				senderStream.setBroadcastAndPrivateDestinationHosts(conference.getList(),conference.getPrivateTalkClient());
			}
			else{
				senderStream.setBroadcastAndPrivateDestinationHosts(conference.getList(),conference.getPrivateTalkClient());
			}
			//for normal participant in conference: do nothing
			// - already listening on the right port
		}
	}
	
	/**
	*
	* updates the GUI button "Connect" to "Disconnect" and removes the nickname and server ip text fields if the user is connected
	* do the invert if the user is disconnected.
	* 
	*/
	public void switchConnectDisconnect(boolean isConnected){
		if(isConnected){
			btConnect.setText("Disconnect");
			tfIpServer.setVisible(false);
			tfNickname.setVisible(false);
		}else{
			btConnect.setText("Connect");
			tfIpServer.setVisible(true);
			tfNickname.setVisible(true);
		}
	}
}
